
/*============================数据库相关==================================*/

/*查看所有数据库*/
show databases;
show dbs;
/*查看当前所在的数据库*/
db;

/*选择数据库, 如果数据库不存在,会自动隐式创建*/
use test;

/*删除数据库*/
db.dropDatabase("crud");
/*创建数据库*/
use test;

/*============================数据库相关==================================*/



/*============================集合相关==================================*/

/*查看当前数据库中的所有集合*/
show collections;

/*删除当前数据库中的集合: 以下逻辑等价*/
db.getSiblingDB("test").getCollection("student").drop(); /*指定数据库*/
db.getSiblingDB("test").student.drop(); /*指定数据库*/
db.getCollection("student").drop(); /*不指定数据库*/
db.student.drop() /*直接使用集合名称删除*/

/*在当前数据库中创建集合, 计算不创建集合，插入数据时也会自动隐式创建：以下逻辑等价*/
db.getSiblingDB("test").createCollection("student");
db.createCollection("student");
/*============================集合相关==================================*/




/*============================文档操作==================================*/
//======== 在指定数据库后，以下四种使用集合的方式都是等价的，在增删改查中都可以使用，后续的样例都使用最后一种方式
db.getSiblingDB("test").getCollection("student"); /*指定数据库，用法获取集合*/
db.getSiblingDB("test").student; /*指定数据库，但是直接使用集合*/
db.getCollection("student");  /*不指定数据库，用法获取集合*/
db.student; /*直接使用集合*/


// ===========================插入文档==================

/*向集合中插入文档：插入单条和多条，必须是json格式数据，文档json的key可以带引号，也可以不带引号，key 中存在空格等特殊字符时需要用双引号包裹
  Mongo会自动给每条文档生成唯一的 _id 键(24个16进制字符,12个字节) ，也可以自定义 _id 的值，只要给插入的json数据增加 "_id" 键即可覆盖，但是不推荐这样做。
  自动生成的唯一的 _id 由如下及部分组合成：[时间戳(8个16进制字符，4个字节) + 机器标识(6个16进制字符，3个字节) + PID(进程ID，4个16进制字符，2个字节) + 计数器(6个16进制字符，3个字节)]，共24个16进制字符，12个字节。
*/

/*1、插入单条 insertOne(json) ：以下逻辑是等价的*/
db.student.insertOne({}); // 插入空的文档，表现为空行
db.student.insertOne({stu_name: "s1", age: 21, gender:"男", tags:["Java", "C"]}); /*插入 json 文档*/
db.student.insertOne({_id: 1,stu_name: "s1", age: 21, gender:"男", tags:["Java", "C"]}) /*自己填写 _id 键*/

/*2、插入多条 insertMany(json数组) ：以下逻辑是等价的*/
db.student.insertMany([{}]); // 插入空的文档，表现为空行
db.student.insertMany([
        {stu_name: "张三", age: 21, gender:"男", tags:["Java", "C"]},
        {stu_name: "李四", age: 22, gender:"男", tags:["Java", "C++"]},
        {stu_name: "王五", age: 23, gender:"男", tags:["Java", "C", "PHP"]},
        {stu_name: "赵六", age: 24, gender:"男", tags:["Java", "C", "Go"]},
        {stu_name: "Jane", age: 25, gender:"女", tags:["Java", "Python"]},
        {stu_name: "Hanmeimei", age: 25, gender:"女", tags:["Java", "C#"]},
    ]
); // 插入 json文档数组


/*3、循环插入*/
for(let i = 1; i <= 10; i++){
	db.student.insertOne({stu_name: "S" + i, age: 24 + i, gender:"男", tags:["Java", "C"]});
}



// ============================查询文档==================
/*
查询：
    db.集合名.find(query, projection)：
        query：用于查找文档的查询条件，json格式。默认为 {}，即匹配所有文档。
        projection（可选）：指定返回结果中包含或排除的字段，json格式。
格式：

    db.集合名.find({条件}); // 按照条件查询所有满足条件的数据
    db.集合名.find({}); 或 db.集合名.find(); // 没有条件，则按查询集合中的全部文档
    // 单个条件，没有运算符就默认表示等于查询
    db.集合名.find({键1: 值});
    // 单个条件，指定运算符查询
    db.集合名.find({键1: {运算符: 值}});
    // 多个条件，条件之间默认是 AND 关系
    db.集合名.find({键1: {运算符: 值}, 键2: {运算符: 值}, 键3: 值});
    // 多个条件，手动指定条件之间的关系
    db.集合名.find({
                    $and/$or/$not: [
                        {键1: {运算符: 值}}, {键2: {运算符: 值}}, {键3: 值}
                    ]
               });
=注意=：同一个key如果要写多个判断条件，请按如下方式指定：
        {键1: {运算符1: 值1, 运算符2: 值2, 运算符3: 值3}}, 或者 {$and: [{键1: {运算符1: 值1}}, {键1: {运算符2: 值2}}]}
     如果使用 {键1: {运算符1: 值1}, 键1: {运算符2: 值2}, 键1: 值3} 这种写法只有最后一条件生效，前面的失效。

运算符：
    "$gt": 大于
    "$gte": 大于等于
    "$lt": 小于
    "$lte": 小于等于
    "$eq": 等于
    "$ne": 不等于
    "$in": in
    "$nin": not in
    "$and": 与
    "$or": 或
    "$not": 非
    "$regex": 正则，格式是JavaScript格式的正则表达式
    "$exists": 判断字段是否存在，（ {age:{$exists:true}：文档中存在 age 字段的）
	"$type": 判断字段的 BSON 类型，（ {age:{$type:"int"}}：文档中存在 age 字段的类型是 int）
    "$all": 数组包含所有指定的元素, （ {tags:{$all: ["Java", "Go"]}} ：文档中 tags 字段的数组中包含 Java、Go 元素 ）
	"$size": 数组的长度等于指定值, （ {tags:{$size: 3}}：文档中 tags 字段的数组长度是 3 ）
*/

/*查询全部*/
db.student.find({});
db.student.find();
db.student.find();

/*单个条件，没有运算符就默认表示等于查询
查询符合 < _id=66e6ab04ac515662a8c64850 > 条件的文档
*/
db.student.find({_id: ObjectId("66e6ab04ac515662a8c64850")});
db.student.find({age: 22});

/*单个条件，指定运算符查询
查询符合 < age 在 [22,23,24,25] > 条件的文档
*/
db.student.find({age: {$in: [22,23,24,25]}});

//查询符合 < stu_name 以 S或s 开头，并且 age > 28 > 条件的文档
db.student.find({stu_name: {$regex: /^S/i}});

/*多个条件，条件之间默认是 AND 关系
查询符合 < age 在 [22,23,24,25] 且 age > 23 且 gender=男 > 条件的文档
*/
db.student.find({age: {$in: [22,23,24,25], $gt: 23}, gender: "男"}); // 正确写法
//db.student.find({age: {$in: [22,23,24,25]}, age: {$gt: 22}, age: {$gt: 23}, gender: "男"}); // 错误写法，只有最后一个 age 条件生效，前一个 age 条件失效


/*多个条件，手动指定条件之间的关系
查询符合 < age 在 [22,23,24,25] 且 age > 22 且 gender=男 > 条件的文档
*/
db.student.find({
                $and: [
                    {age: {$in: [22,23,24,25]}},
                    {age: {$gt: 22}},
                    {gender: "男"}
                ]
           });

//查询 < (age 在 [22,23,24] 且 age > 22 且 gender=男) OR (gender=女) > 的文档
db.student.find({
                $or: [
                    {
                        $and: [
                            {age: {$in: [22,23,24], $gt: 22}},
                            {gender: "男"}
                        ]
                    },
                    {gender: "女"}
                ]
           });

//查询 < tags长度为3，且包含 ”Java“ > 的文档
db.student.find({tags: {$all:["Java"], $size:3}});
//db.student.find({tags: {$all:["Java"], $size:3, $type:"array"}, age: { $exists: true}});


/*
投影：控制查询出哪些字段，但是 _id 是默认查出的, 可以手动禁止
只查询出指定的字段，默认查询出全部的字段，指定find的第二个参数后，只会查询出指定的字段
字段key: 1 查询出，0不查询出
*/
db.student.find({age:{$gt:25}}, {stu_name:1, age: 1, _id:0});


/*
cout, skip, limit
*/
//获取查询结果的数量
db.student.find().count();
//返回查询结果的前多少条文档
db.student.find({age:{$gt:25}}).limit(2);
//跳过查询结果的前多少条文档，返回之后的文档
db.student.find({age:{$gt:25}}).skip(2);
//分页：跳过查询结果的前多少条文档,然后取之后的多少行
db.student.find({age:{$gt:25}}).skip(2).limit(10);
db.student.find({age:{$gt:25}}).skip(2).limit(10).count();


/*
排序 sort({key1:1,key2:-1})
1: 升序，-1: 降序

先按照 age 升序，再按照 stu_name 降序
*/
db.student.find({age:{$gt:23}},{stu_name:1, age: 1, _id:0})
            .sort({age:1, stu_name:-1});




/*使用 SQL 查询，DataGrip 工具自动将 SQl 语句转为符合 Mongo语法的语句
select * from student where stu_name like 'S%' and age > 20;
*/
db.student.find({$and: [{"stu_name": {$regex: /四$/}}, {"age": {$gt: 20}}]});

// select * from student where _id=ObjectID('66e67f3ca6f0e12066e2c45f')
db.getCollection("student").find({"_id": {$eq: new ObjectId('66e67f3ca6f0e12066e2c45f')}});





// ============================删除文档==================
/*
删除：
    //删除一条：按照文档在集合中的存储顺序来查找第一个符合条件的文档，并将其删除
    db.student.deleteOne({条件}); // 一条
    db.student.deleteOne({}); // 没有条件，默认为真，删除一条
    //删除多条：按照文档在集合中的存储顺序来查找所有符合条件的文档，并将其全都删除
    db.student.deleteMany({条件}); // 多条
    db.student.deleteMany({}); // 没有条件，默认为真，删除集合中的所有文档
    //删除一条并返回删除的文档
    db.collection.findOneAndDelete(filter, options)
        filter：用于查找要删除的文档的查询条件。
        options：可选参数对象，如 查询哪些字段：{projection:{age:1,stu_name:1,tags:0}}, 按照什么排序后再删除{sort:{age:1,stu_name:-1}} 等
        目前不知道什么原因，options 没有生效？？？


格式：条件的格式和 find() 中的格式一致
*/

/*1、删除一条：*/
// 删除第一条文档
db.student.deleteOne({});
//删除第一条符合 < (age 在 [22,23,24] 且 age > 22 且 gender=男) OR (gender=女) > 的文档
db.student.deleteOne({
                    $or: [
                        {
                            $and: [
                                {age: {$in: [22,23,24], $gt: 22}},
                                {gender: "男"}
                            ]
                        },
                        {gender: "女"}
                    ]
                });


/*2、删除多条：*/
// 删除所有文档
db.student.deleteMany({});
//删除所有符合 < (age 在 [22,23,24] 且 age > 22 且 gender=男) OR (gender=女) > 的文档
db.student.deleteMany({
                    $or: [
                        {
                            $and: [
                                {age: {$in: [22,23,24], $gt: 22}},
                                {gender: "男"}
                            ]
                        },
                        {gender: "女"}
                    ]
                });


/*3、排序查询后，根据查询结果进行删除：*/
// 3.1 方式1：迭代器
let cursor  = db.student.find({age:{$gt:20}})
                        .sort({age:-1, stu_name:1})
                        .limit(2);
// 遍历迭代器
while (cursor.hasNext()) {
    let doc = cursor.next();
    // 打印结果
    console.log(doc);
    // 删除文档
    db.student.deleteOne({_id: doc._id});
}

// 3.2 方式2：数组
let docs = db.student.find({age:{$gt:20}})
                     .sort({age:-1, stu_name:1})
                     .limit(2).toArray();
// 遍历查询结果数组
docs.forEach(function(doc) {
    // 打印结果
    console.log(doc);
    // 删除文档
    db.student.deleteOne({_id: doc._id});
});


//============================更新文档==================
/*
更新文档：
    db.collection.updateOne(filter, update, options)
        filter：过滤条件，json格式，和find操作中的条件格式要求一致
        update：更新操作符，json格式，样例 {操作符: {键: 值}}
        options：可选参数对象，如 upsert、arrayFilters 等，json格式

    操作符：
        $set：给一个或多个字段设置值，字段不存在就自动新增该字段
        $unset：删除文档中的某一个或多个字段
        $inc：给数值字段递增指定的数值，字段不存在就自动新增该字段
        $push：向数组字段追加一个或多个值，字段不存在就自动新增该字段
        $pull：从数组字段中移除一个值
        $pullAll：从数组字段中移除多个值
        $rename：重命名文档中的一个字段
        $currentDate：将字段设置为当前日期时间
        $min 和 $max：设置字段的最小或最大值
        $addToSet：向数组字段追加一个或多个唯一值
样例：
    //按照存储顺序，只更新满足条件的第一条文档：
    db.collection.updateOne(filter, update, options)
    //更新满足多条的所有文档：
    db.student.updateMany(filter, update, options);
    // 更新第一条文档并返回更新前的文档
    db.student.findOneAndUpdate(filter, update, options);

*/
/* 当 age > 25 时将,将tags数组的值设置为["Java","Perl"]*/
db.student.updateOne(
        {age:{$gt:25}},
        {$set:{tags:["Java","Perl"]}}
);

/* 当 age >= 44 时将，移除tags，stu_name字段*/
db.student.updateOne(
        {age:{$gte:44}},
        {$unset:{tags:"", stu_name:""}}
);
/* 当 stu_name 以 S或s开头 时将，age+2, 同时向tags数组中添加一个元素*/
db.student.updateOne(
        {stu_name:{$regex:/^S/i}},
        {$inc:{age: 2}, $push:{tags:"Kotlin"}}
);
/* 当 age > 25 时将，age+2, tags数组中添加一个数组元素*/
db.student.updateOne(
        {age:{$gt:25}},
        {$inc:{age: 2}, $push:{tags:["Kotlin","Rust"]}}
);
/* 当 age > 25 时将，age+2, tags数组中添加多个元素，如果数据已有该元素也会再次添加*/
db.student.updateOne(
        {age:{$gt:25}},
        {$inc:{age: 2}, $push:{tags:{$each:["Kotlin","Rust"]}}}
);
/* 当 age > 25 时将，age+2, tags数组中添加多个元素，如果数据已有该元素，则不会再次添加*/
db.student.updateOne(
        {age:{$gt:25}},
        {$inc:{age: 2}, $addToSet:{tags:{$each:["Kotlin","Rust"]}}}
);
/* 当 stu_name 以 S或s开头 时将，age+2, 同时移除tags数组中的 所有的“Kotlin” 元素*/
db.student.updateOne(
        {stu_name:{$regex:/^S/i}},
        {$inc:{age: 2}, $pull:{tags:"Kotlin"}}
);
/* 当 stu_name 以 S或s开头 时将，age+2, 同时移除tags数组中的 所有的“Kotlin”、"Java" 元素*/
db.student.updateMany(
        {stu_name:{$regex:/^S/i}},
        {$inc:{age: 2}, $pullAll:{tags:["Kotlin", "Java"]}}
);

/* 当 stu_name 以 S或s开头 时将，age-4*/
db.student.updateMany(
        {stu_name:{$regex:/^S/i}},
        {$inc: {age: -4}}
);

/*给所有文档新增一个字段*/
db.student.updateMany(
        {},
        {$set:{age1:22}}
);

/*给所有文档一个字段重命名成 age2*/
db.student.updateMany(
        {},
        {$rename:{age1:"age2"}}
);

/*移除所有文档的一个字段*/
db.student.updateMany(
        {},
        {$unset:{age1:"", age2:""}}
);

/* 当 stu_name 以 S或s开头 时将，age-4*/
db.student.findOneAndUpdate(
        {stu_name:{$regex:/^S/i}},
        {$inc: {age: -4}}
);


/*============================文档操作==================================*/